package splab.ufcg.edu.br.trace.querier;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;

import splab.ufcg.edu.br.trace.entities.ApacheSolrJFieldEnum;
import splab.ufcg.edu.br.trace.entities.TraceLink;
import splab.ufcg.edu.br.trace.entities.TraceLinkList;
import splab.ufcg.edu.br.trace.exceptions.QueryException;
import splab.ufcg.edu.br.trace.exceptions.TraceLinkException;
import splab.ufcg.edu.br.trace.interfaces.Searchable;
import splab.ufcg.edu.br.trace.query.entities.QueryParameter;
import splab.ufcg.edu.br.trace.search.util.ConnectionProperties;

public class ApacheSolrJQuerier implements Searchable {

	public ApacheSolrJQuerier(ConnectionProperties apacheConnection) {
		
		this.connectionProperties = apacheConnection;
		
	}

	public ApacheSolrJQuerier() {

	}

	public ConnectionProperties getConnectionProperties() {
		return connectionProperties;
	}

	public void setConnectionProperties(ConnectionProperties connectionProperties) {
		this.connectionProperties = connectionProperties;
	}

	private ConnectionProperties connectionProperties;

	@Override
	public TraceLinkList queryTraceLins(QueryParameter... queryParameter)
			throws QueryException {

		QueryResponse response = null;
		SolrDocumentList results = null;
		TraceLinkList tracelinks = new TraceLinkList();

		try {
			HttpSolrServer server = new HttpSolrServer(
					this.connectionProperties.getConnectionUrl());

			SolrQuery query = new SolrQuery();
			query.setQuery("*:*");

			if (queryParameter != null && queryParameter.length > 0) {
				query.setFilterQueries(this.getFieldParameters(queryParameter));
			}

			query.setFields(ApacheSolrJFieldEnum.REQUIREMENT.toString(),
					ApacheSolrJFieldEnum.SEMANTIC.toString(),
					ApacheSolrJFieldEnum.ARTIFACT_TYPE.toString(),
					ApacheSolrJFieldEnum.ARTIFACT.toString());

			query.setStart(0);
			query.set("defType", "edismax");
			query.setRows(1000000);
			response = server.query(query);
			results = response.getResults();
			tracelinks = this.getTraceLinksFromQueryResult(results);
		} catch (SolrServerException ex) {
			StringBuilder message = new StringBuilder();
			message.append("Unexpected error while querying database");

			throw new QueryException(message.toString(), ex);
		} catch (TraceLinkException ex) {

			StringBuilder message = new StringBuilder();
			message.append("Unexpected error while querying database");

			throw new QueryException(message.toString(), ex);

		}

		return tracelinks;
	}

	private TraceLinkList getTraceLinksFromQueryResult(SolrDocumentList results)
			throws TraceLinkException {

		TraceLinkList tracelinks = new TraceLinkList();

		if (results == null) {
			return tracelinks;
		}

		Iterator<SolrDocument> resultIterator = results.iterator();
		while (resultIterator.hasNext()) {
			SolrDocument document = resultIterator.next();
			String requirement = document.getFieldValue(
					ApacheSolrJFieldEnum.REQUIREMENT.toString()).toString();
			String semantic = document.getFieldValue(
					ApacheSolrJFieldEnum.SEMANTIC.toString()).toString();
			String type = document.getFieldValue(
					ApacheSolrJFieldEnum.ARTIFACT_TYPE.toString()).toString();
			String artifact = document.getFieldValue(
					ApacheSolrJFieldEnum.ARTIFACT.toString()).toString();

			TraceLink traceLink = new TraceLink(requirement, semantic, type,
					artifact);

			tracelinks.add(traceLink);

		}

		return tracelinks;
	}

	private String getFieldParameters(QueryParameter... queryParameter) {
		StringBuilder filter = new StringBuilder();
		Map<String, List<String>> mapFieldValues = new HashMap<String, List<String>>();
		
		for(QueryParameter param: queryParameter){
			
			if (!mapFieldValues.containsKey(param.getField())){
				mapFieldValues.put(param.getField().toString(), new ArrayList<String>());
			}
			
			mapFieldValues.get(param.getField()).add(param.getValue());
		}
		
		int i = 0;
		for(String field: mapFieldValues.keySet()){
			if (i > 0){
				filter.append(", ");
			}
			
			filter.append(field);
			filter.append(":");
			
			List<String> values = mapFieldValues.get(field);
			
			if (values.size() > 0){
				filter.append("(");
				int j = 0;
				for(String value: values){
					if (j > 0){
						filter.append(" ");
					}
					filter.append("\"");
					filter.append(value);
					filter.append("\"");;
					j++;
				}
				
				filter.append(")");
				
			} else {
				filter.append(values.get(0));				
			}
			
			i++;
		}

		return filter.toString();
	}

}
